package com.yanqu.road.server.gameplayer.module.grandson;

import com.alibaba.fastjson.JSONObject;
import com.yanqu.road.dao.db.DBManager;
import com.yanqu.road.dao.db.DatabaseMgr;
import com.yanqu.road.dao.db.DbNameStrategy;
import com.yanqu.road.dao.impl.player.GrandChildDataDaoImpl;
import com.yanqu.road.dao.impl.player.GrandChildTalentBookDaoImpl;
import com.yanqu.road.dao.impl.player.PupiDataDaoImpl;
import com.yanqu.road.dao.impl.player.SchoolSiteDaoImpl;
import com.yanqu.road.entity.config.goods.GoodsInfo;
import com.yanqu.road.entity.config.skill.SkillInfo;
import com.yanqu.road.entity.config.skill.SkillUpgradeInfo;
import com.yanqu.road.entity.enums.*;
import com.yanqu.road.entity.grandchild.*;
import com.yanqu.road.entity.log.*;
import com.yanqu.road.entity.marriage.MarriageRecord;
import com.yanqu.road.entity.player.UserPatrons;
import com.yanqu.road.entity.retrievereward.config.RecoveryRewardParameterConfig;
import com.yanqu.road.entity.servercenter.GameEditLimit;
import com.yanqu.road.logic.bussiness.grandchild.GrandChildBussiness;
import com.yanqu.road.logic.bussiness.marriage.MarriageBusiness;
import com.yanqu.road.logic.config.GameConfig;
import com.yanqu.road.pb.activity.RecoveryRewardProto;
import com.yanqu.road.pb.grandchild.GrandChildProto;
import com.yanqu.road.server.GameServer;
import com.yanqu.road.server.gameplayer.GamePlayer;
import com.yanqu.road.server.gameplayer.module.base.GeneralModule;
import com.yanqu.road.server.gameplayer.module.patrons.PatronsModule;
import com.yanqu.road.server.gameplayer.module.player.AttributeModule;
import com.yanqu.road.server.gameplayer.module.player.BagModule;
import com.yanqu.road.server.gameplayer.module.player.CurrencyModule;
import com.yanqu.road.server.gameplayer.module.retrieveReward.RecoveryRewardModule;
import com.yanqu.road.server.gameplayer.module.xiaomian.XiaoMianModule;
import com.yanqu.road.server.manger.*;
import com.yanqu.road.server.manger.config.*;
import com.yanqu.road.server.manger.config.grandchild.GrandChildConfigMgr;
import com.yanqu.road.server.manger.config.grandchild.GrandChildMgr;
import com.yanqu.road.server.manger.config.grandchild.talent.entity.TalentTriggerResult;
import com.yanqu.road.server.manger.config.grandchild.talent.enums.eGrandChildSkillEffectStage;
import com.yanqu.road.server.manger.config.grandchild.talent.utils.TalentResultUtils;
import com.yanqu.road.server.manger.config.grandchild.talent.utils.TalentUtils;
import com.yanqu.road.server.manger.log.AutoLogMgr;
import com.yanqu.road.server.manger.log.LogMgr;
import com.yanqu.road.server.manger.maidian.grandchild.GrandChildLog;
import com.yanqu.road.server.manger.rank.RankMgr;
import com.yanqu.road.server.pb.GrandChildPb;
import com.yanqu.road.server.pb.PlayerPb;
import com.yanqu.road.server.protocol.ClientProtocol;
import com.yanqu.road.server.protocol.CrossProtocol;
import com.yanqu.road.server.protocol.GameErrorCode;
import com.yanqu.road.server.protocol.Protocol;
import com.yanqu.road.utils.UUIDHelper;
import com.yanqu.road.utils.date.DateHelper;
import com.yanqu.road.utils.property.Property;
import com.yanqu.road.utils.property.PropertyHelper;
import com.yanqu.road.utils.string.StringUtils;

import java.math.BigInteger;
import java.sql.PreparedStatement;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.atomic.AtomicLong;
import java.util.stream.Collectors;

public class GrandChildModule extends GeneralModule {

    //能度蜜月的记录
    private Map<String, MarriageRecord> marriageMap = new ConcurrentHashMap<>();

    private Map<String, GrandChildData> grandChildDataMap = new ConcurrentHashMap<>();

    private Map<Integer, List<SchoolSiteData>> schoolSiteMap = new ConcurrentHashMap<>();

    private Map<String, Integer> lastSaveTimePowerMap = new ConcurrentHashMap<>();

    /**
     * 只用来展示门客的门生信息
     */
    private Map<Integer, List<GrandChildData>> patronsShowChild = new ConcurrentHashMap<>();

    /**
     * 门客的门生统计
     */
    private Map<Integer, Map<Integer, PupilData>> patronsPupilMap = new ConcurrentHashMap<>();

    /**
     * 孙子职业集合
     */
    private Map<Integer, GrandChildProfession> grandChildProfessionMap = new ConcurrentHashMap<>();

    /**
     * 孙子职业最强集合
     */
    private Map<Integer, GrandChildData> grandChildProfessionTopMap = new ConcurrentHashMap<>();
    private GrandChildData topGrandChild;

    /**
     * 孙子科举官职集合
     */
    private Map<Integer, GrandChildOfficial> grandChildOfficialMap = new ConcurrentHashMap<>();

    private Map<Integer, GrandChildTalentBook> talentBookMap = new ConcurrentHashMap<>();

    /**
     * 缓存职业孙子信息
     */
    private Map<Integer, List<GrandChildData>> professionGrandChildListMap = new ConcurrentHashMap<>();

    private List<MarriageRecord> needUpdateMarriageRecordList = new ArrayList<>();

    public Map<Integer, List<SchoolSiteData>> getSchoolSiteMap() {
        return schoolSiteMap;
    }

    public GrandChildModule(GamePlayer player) {
        super(player);
    }


    public int doHoneyMoon(String childId) {
        MarriageRecord record = marriageMap.get(childId);
        if (record == null || !record.isCanHoneyMoon()) {
            //不能度蜜月
            return GameErrorCode.E_HONEY_MOON_ALREADY;
        }

        //判断数量
        if (getUnGraduationChildNum() >= GameConfig.GRAND_CHILD_MAX_NUM) {
            return GameErrorCode.E_GRAND_CHILD_UN_GRADUATION_NUM_OVER_LIMIT;
        }
        //消耗道具
        Property honeyMoonConsume = GrandChildConfigMgr.getHoneyMoonConsume();
        if (!player.getModule(CurrencyModule.class).removeCurrency(honeyMoonConsume, eLogMoneyType.GrandChild, eLogMoneyType.HoneyMoon)) {
            //触发现金礼包
            player.notifyListener(eGamePlayerEventType.RechargeGrandChild.getValue(), 0);
            player.notifyListener(eGamePlayerEventType.RechargeGrandChildNew.getValue(), 0);
            return GameErrorCode.E_BAG_ITEM_NO_ENOUGH;
        }
        //设置为不能生了
        record.setCanHoneyMoon(false);
        addToNeedUpdateRecordList(record);
        //开始生
        GrandChildData childData = makeNewGrandChild(record);
        grandChildDataMap.put(childData.getId(), childData);
        syncGrandChildMsg(Collections.singletonList(childData));
        GrandChildProto.HoneyMoonRespMsg.Builder resp = GrandChildProto.HoneyMoonRespMsg.newBuilder();
        resp.setRet(0);
        resp.setGrandChild(GrandChildPb.parseGrandChildMsg(childData));
        player.sendPacket(ClientProtocol.U_HONEY_MOON, resp);

        //平台接口数据
        player.getModule(XiaoMianModule.class).addGrandChildCount(1);

        // 扣除一个记录
        marriageRecordCount.decrementAndGet();

        // 日志
        AutoLogMgr.add(new LogGrandChildBirth(
                getUserId(),
                childData.getId(),
                childData.getBaseQuality(),
                childData.getBaseFlairList(),
                childData.getOptionalList(),

                record.getChildTalent(),
                record.getPartnerChildTalent(),
                record.getChildEarnSpeed(),
                record.getPartnerChildEarnSpeed(),
                record.getTotalChildEarnSpeed()
        ));

        // 日志
        AutoLogMgr.add(new LogGrandChildRefreshTalent(
                getUserId(),
                childData.getId(),
                new ArrayList<>(), // 出生也记录一条天赋刷新
                childData.getOptionalList()
        ));

        //埋点
        //GrandChildLog.getGrandson(player, childData);

        if (!player.getModule(CurrencyModule.class).currencyIsEnough(honeyMoonConsume)) {
            //触发现金礼包
            player.notifyListener(eGamePlayerEventType.RechargeGrandChildNew.getValue(), 0);
        }
        return 0;
    }

    public int checkDoHoneyMoonOneKey(String childId) {
        MarriageRecord record = marriageMap.get(childId);
        if (record == null || !record.isCanHoneyMoon()) {
            //不能度蜜月
            return GameErrorCode.E_HONEY_MOON_ALREADY;
        }

        //判断数量
        if (getUnGraduationChildNum() >= GameConfig.GRAND_CHILD_MAX_NUM) {
            return GameErrorCode.E_GRAND_CHILD_UN_GRADUATION_NUM_OVER_LIMIT;
        }
        //消耗道具
        Property honeyMoonConsume = GrandChildConfigMgr.getHoneyMoonConsume();
        if (!player.getModule(CurrencyModule.class).currencyIsEnough(honeyMoonConsume)) {
            //触发现金礼包
            player.notifyListener(eGamePlayerEventType.RechargeGrandChild.getValue(), 0);
            return GameErrorCode.E_BAG_ITEM_NO_ENOUGH;
        }
        return 0;
    }

    public String doHoneyMoonOneKey(String childId) {
        MarriageRecord record = marriageMap.get(childId);
        //消耗道具
        Property honeyMoonConsume = GrandChildConfigMgr.getHoneyMoonConsume();
        player.getModule(CurrencyModule.class).removeCurrency(honeyMoonConsume, eLogMoneyType.GrandChild, eLogMoneyType.HoneyMoon);
        record.setCanHoneyMoon(false);
        addToNeedUpdateRecordList(record);
        GrandChildData childData = makeNewGrandChild(record);
        grandChildDataMap.put(childData.getId(), childData);
        syncGrandChildMsg(Collections.singletonList(childData));

        //平台接口数据
        player.getModule(XiaoMianModule.class).addGrandChildCount(1);

        // 扣除一个记录
        marriageRecordCount.decrementAndGet();

        // 日志
        AutoLogMgr.add(new LogGrandChildBirth(
                getUserId(),
                childData.getId(),
                childData.getBaseQuality(),
                childData.getBaseFlairList(),
                childData.getOptionalList(),

                record.getChildTalent(),
                record.getPartnerChildTalent(),
                record.getChildEarnSpeed(),
                record.getPartnerChildEarnSpeed(),
                record.getTotalChildEarnSpeed()
        ));

        // 日志
        AutoLogMgr.add(new LogGrandChildRefreshTalent(
                getUserId(),
                childData.getId(),
                new ArrayList<>(), // 出生也记录一条天赋刷新
                childData.getOptionalList()
        ));

        //埋点
        //GrandChildLog.getGrandson(player, childData);

        return childData.getId();
    }

    private void addToNeedUpdateRecordList(MarriageRecord record) {
        synchronized (needUpdateMarriageRecordList) {
            needUpdateMarriageRecordList.add(record);
        }
    }

    /**
     * 获取未出师的孙子数量
     *
     * @return
     */
    private int getUnGraduationChildNum() {
        int num = 0;
        for (GrandChildData data : grandChildDataMap.values()) {
            if (data.getLv() < data.getMaxLv()) {
                num++;
            }
        }
        return num;
    }

    private GrandChildData makeNewGrandChild(MarriageRecord record) {
        //品质 天资 天赋列表
        GrandChildData childData = new GrandChildData();
        childData.setUserId(getUserId());
        childData.setId(UUIDHelper.randomUUID(String.valueOf(GameServer.getInstance().getServerId())));
        childData.setLv(1);
        childData.setSex(ChildMgr.getRandomChildSex(100));
        childData.setNickName(ChildMgr.getRandomChildName(childData.getSex(), player.getLanguage()));
        childData.setBeautyId(record.getMotherId());
        GrandChildConfigMgr.initQualityAndMaxLv(player, record, childData);
        childData.setBaseQuality(childData.getQuality());
        List<Integer> newFlairList = GrandChildConfigMgr.initFlairList(player,record, childData);
        childData.setFlairList(newFlairList);
        childData.setBaseFlairList(new ArrayList<>(newFlairList));
        childData.setOptionalList(GrandChildConfigMgr.randomTalentList(player, childData));
        addTalentBook(childData.getOptionalList());
        GrandChildConfigMgr.confirmOccupation(childData);
        childData.setInsertOption();
        return childData;
    }


    public int saveGrandChild(String id, String nickName, List<Integer> talentList) {
        GrandChildData childData = grandChildDataMap.get(id);
        if (childData == null) {
            return 1;
        }
        List<Integer> optionalList = childData.getOptionalList();
        if (!optionalList.containsAll(talentList)) {
            //选择的天赋不在可选天赋列表内
            return GameErrorCode.E_GRAND_CHILD_TALENT_OPTIONAL_ERROR;
        }
        //校验下昵称是否合法 也顺便判断下是不是已经保存过了
        if (childData.getOptionalList().size() == 0) {
            return GameErrorCode.E_GRAND_CHILD_CONFIRM_ALREADY;
        }
        int checkNameRet = NameMgr.checkNickname(ServerListMgr.getChannelId(GameServer.getInstance().getServerId()), nickName);
        if (checkNameRet != 0) {
            return checkNameRet;
        }
        if (new HashSet<>(talentList).size() != GrandChildConfigMgr.getTalentNumLimit(player)) {
            //不能多选 也不能少选
            return GameErrorCode.E_GRAND_CHILD_TALENT_OVER_LIMIT;
        }
        //编辑限制
        GameEditLimit gameEditLimit = GameEditLimitMgr.getGameEditLimit(Protocol.S_CONFIRM_GRANDSON_DATA);
        if(null != gameEditLimit){
            long nowTime = System.currentTimeMillis();
            if(nowTime >= gameEditLimit.getLimitEditStartTime() && nowTime < gameEditLimit.getLimitEditEndTime()){
                nickName = childData.getNickName();
            }
        }
        childData.setNickName(nickName);
        childData.setTalentList(talentList);
        childData.setOptionalList(new ArrayList<>());
        GrandChildConfigMgr.grandChildBorn(player, childData, true);
        syncGrandChildMsg(Collections.singletonList(childData));
        return 0;
    }


    public void addMarriageRecord(MarriageRecord record) {
        BigInteger totalChildEarnSpeed = record.getTotalChildEarnSpeed();
        if (totalChildEarnSpeed.compareTo(canHoneyMoonEarnSpeed()) >= 0 && SystemOpenMgr.systemOpen(player,eSystemId.GrandChild.getValue())) {
            record.setCanHoneyMoon(true);
            marriageMap.put(record.getChildUid(), record);

            marriageRecordCount.incrementAndGet();
        }
    }

    public BigInteger canHoneyMoonEarnSpeed() {
        return new BigInteger(GameConfig.GRAND_CHILD_HONEYMOON_EARN_SPEED);
    }


    private long lastRefreshMarriageRecordTime = 0;

    /**
     * 获取可以度蜜月的列表
     *
     * @return 可以度蜜月的列表
     */
    public void getHoneyMoonList(boolean isRefresh, long occupation) {
        GrandChildProto.StoneRespMsg.Builder resp = GrandChildProto.StoneRespMsg.newBuilder();
        if (isRefresh) {
            long nowTime = System.currentTimeMillis();
            if (nowTime - lastRefreshMarriageRecordTime < DateHelper.SECOND_MILLIONS) {
                resp.setRet(GameErrorCode.E_GRAND_CHILD_REFRESH_TOO_FAST);
                player.sendPacket(ClientProtocol.U_STONE_LIST_REQ, resp);
                return;
            }
            lastRefreshMarriageRecordTime = nowTime;
            player.getModule(AttributeModule.class).setAttribute(ePlayerAttrType.GrandChildRefreshOccupation, occupation);
            // 先处理未入库数据
            saveMarriageRecord();
            // 刷新下列表
            refreshMarriageRecord();
        }
        resp.setRet(0);
        resp.setTotalRecord(marriageRecordCount.get());
        resp.setOccupation(player.getModule(AttributeModule.class).getAttribute(ePlayerAttrType.GrandChildRefreshOccupation));
        // 获取列表
        List<MarriageRecord> records = getCanHoneyMoonList();
        for (MarriageRecord record : records) {
            GrandChildProto.MarriageRecordMsg.Builder tempMsg = GrandChildPb.parseMarriageRecordTempMsg(record);
            resp.addMarriageRecord(tempMsg);
        }
        player.sendPacket(ClientProtocol.U_STONE_LIST_REQ, resp);
    }

    private List<MarriageRecord> getCanHoneyMoonList() {
        // 指定职业
        long refreshOccupation = player.getModule(AttributeModule.class).getAttribute(ePlayerAttrType.GrandChildRefreshOccupation);
        List<MarriageRecord> temp = new ArrayList<>(marriageMap.values());
        List<MarriageRecord> records = new ArrayList<>();
        for (MarriageRecord record : temp) {
            if (refreshOccupation != 0 && record.getChildOccupation() != refreshOccupation && record.getPartnerChildOccupation() != refreshOccupation) {
                // 不满足筛选条件
                continue;
            }
            if (record.isCanHoneyMoon()) {
                records.add(record);
            }
        }
        records.sort((o1, o2) -> o2.getTotalChildEarnSpeed().compareTo(o1.getTotalChildEarnSpeed()));
        return records;
    }

    private int getPageSize() {
        return 10;
    }

    /**
     * 刷新孙子天赋列表
     *
     * @param id
     */
    public int refreshTalent(String id) {
        GrandChildData childData = grandChildDataMap.get(id);
        if (childData == null) {
            return GameErrorCode.E_GRAND_CHILD_NO_EXIST;
        }
        //判断是否已经保存
        if (childData.getOptionalList().size() == 0) {
            return GameErrorCode.E_GRAND_CHILD_CONFIRM_ALREADY;
        }
        //计算刷新所需资源
        Property consume = GrandChildConfigMgr.getReTalentConsume(childData.getReTalentTimes());
        if (consume != null) {
            if (!player.getModule(CurrencyModule.class).removeCurrency(consume, eLogMoneyType.GrandChild, eLogMoneyType.ReTalent)) {
                //触发现金礼包
                player.notifyListener(eGamePlayerEventType.RechargeGrandChild.getValue(), 0);
                player.notifyListener(eGamePlayerEventType.RechargeGrandChildNew.getValue(), 0);
                return GameErrorCode.E_BAG_ITEM_NO_ENOUGH;
            }
        }

        List<Integer> logOldTalentList = new ArrayList<>(childData.getOptionalList()); // 日志用

        List<Integer> list = GrandChildConfigMgr.randomTalentList(player, childData);
        addTalentBook(list);
        childData.setOptionalList(list);
        childData.setReTalentTimes(childData.getReTalentTimes() + 1);
        GrandChildProto.ReTalentRespMsg.Builder resp = GrandChildProto.ReTalentRespMsg.newBuilder();
        resp.setRet(0);
        GrandChildProto.GrandChildMsg.Builder childMsg = GrandChildPb.parseGrandChildMsg(childData);
        resp.setGrandChild(childMsg);
        player.sendPacket(ClientProtocol.U_REFRESH_GRANDSON_TALENT_LIST, resp);

        // 日志
        AutoLogMgr.add(new LogGrandChildRefreshTalent(
                getUserId(),
                childData.getId(),
                logOldTalentList,
                childData.getOptionalList()
        ));

        if (consume != null) {
            if (!player.getModule(CurrencyModule.class).currencyIsEnough(consume)) {
                //触发现金礼包
                player.notifyListener(eGamePlayerEventType.RechargeGrandChildNew.getValue(), 0);
            }
        }
        return 0;
    }

    /**
     * 获取可度蜜月列表总数
     */
    private AtomicLong marriageRecordCount = new AtomicLong(0);

    /**
     * 刷新度蜜月记录
     */
    public void refreshMarriageRecord() {
        int refreshLimit = GameConfig.GRAND_CHILD_SHOW_MAX_NUM;
        // 预设孙子列表职业
        long refreshOccupation = player.getModule(AttributeModule.class).getAttribute(ePlayerAttrType.GrandChildRefreshOccupation);
        if (refreshOccupation == 0) {
            // 不指定职业
            marriageMap = MarriageBusiness.getCanMarriageRecordNew(player.getUserId(), refreshLimit);
        } else {
            // 指定职业
            marriageMap = MarriageBusiness.getCanMarriageRecordNew(player.getUserId(), refreshOccupation, refreshLimit);
        }
        // 孙子列表总数
        long canMarriageRecordCount = MarriageBusiness.getCanMarriageRecordCount(player.getUserId());
        marriageRecordCount = new AtomicLong(canMarriageRecordCount);
    }

    @Override
    public boolean loadData() {

        // 数据太大，7.5废弃
//        marriageMap = MarriageBusiness.getCanMarriageRecord(player.getUserId());
        refreshMarriageRecord();

        grandChildDataMap = new GrandChildDataDaoImpl().getGrandChildData(getUserId());
        schoolSiteMap = new SchoolSiteDaoImpl().getSchoolSiteData(getUserId());
        grandChildProfessionMap = GrandChildBussiness.getGrandChildProfessionMap(player.getUserId());
        grandChildOfficialMap = GrandChildBussiness.getGrandChildOfficialMap(player.getUserId());
        patronsPupilMap = new PupiDataDaoImpl().getPupilData(getUserId());
        grandChildProfessionTopMap = new GrandChildDataDaoImpl().getGrandChildProfessionTopMap(player.getUserId());
        lastSaveTimePowerMap = new ConcurrentHashMap<>();

        for (List<SchoolSiteData> siteDataList:schoolSiteMap.values()){
            for (SchoolSiteData schoolSiteData:siteDataList){
                GrandChildData grandChildData = grandChildDataMap.get(schoolSiteData.getChildId());
                if (grandChildData != null){
                    lastSaveTimePowerMap.put(grandChildData.getId(),grandChildData.getArchaeus());
                }
            }
        }

        //==================================修复代码==========================
        for(GrandChildData data : grandChildDataMap.values()){
            if(data.getProfessionId() % 10000 == 40){
                data.setProfessionId(data.getProfessionId() - 1);
                compareProfessionGrandChild(data);
            }
        }
        String sql = "delete from `t_u_grand_child_profession` where userId=? and professionId=?";
        int bugId = 10040;
        int repairId = 10039;
        GrandChildProfession gp10040 = grandChildProfessionMap.get(bugId);
        if(null != gp10040){
            PreparedStatement call = null;
            DBManager db = DatabaseMgr.getDbManager();
            try {
                db.setConnName(DbNameStrategy.getUserDb());
                call = db.getConnection().prepareStatement(sql);
                call.setLong(1, player.getUserId());
                call.setInt(2, bugId);
                if(call.executeUpdate() > 0){//删除成功
                    grandChildProfessionMap.remove(bugId);          //从内存移除
                    addGrandChildProfession(repairId, (int) gp10040.getGrandChildCount());
                }
            } catch (Exception e) {
                log.error(Thread.currentThread().getStackTrace()[1].getMethodName() + ":", e);
            } finally {
                db.closeConnection(call, null);
            }
        }

        bugId = 20040;
        repairId = 20039;
        GrandChildProfession gp20040 = grandChildProfessionMap.get(bugId);
        if(null != gp20040){
            PreparedStatement call = null;
            DBManager db = DatabaseMgr.getDbManager();
            try {
                db.setConnName(DbNameStrategy.getUserDb());
                call = db.getConnection().prepareStatement(sql);
                call.setLong(1, player.getUserId());
                call.setInt(2, bugId);
                if(call.executeUpdate() > 0){//删除成功
                    grandChildProfessionMap.remove(bugId);          //从内存移除
                    addGrandChildProfession(repairId, (int) gp20040.getGrandChildCount());
                }
            } catch (Exception e) {
                log.error(Thread.currentThread().getStackTrace()[1].getMethodName() + ":", e);
            } finally {
                db.closeConnection(call, null);
            }
        }

        bugId = 30040;
        repairId = 30039;
        GrandChildProfession gp30040 = grandChildProfessionMap.get(bugId);
        if(null != gp30040){
            PreparedStatement call = null;
            DBManager db = DatabaseMgr.getDbManager();
            try {
                db.setConnName(DbNameStrategy.getUserDb());
                call = db.getConnection().prepareStatement(sql);
                call.setLong(1, player.getUserId());
                call.setInt(2, bugId);
                if(call.executeUpdate() > 0){//删除成功
                    grandChildProfessionMap.remove(bugId);          //从内存移除
                    addGrandChildProfession(repairId, (int) gp30040.getGrandChildCount());
                }
            } catch (Exception e) {
                log.error(Thread.currentThread().getStackTrace()[1].getMethodName() + ":", e);
            } finally {
                db.closeConnection(call, null);
            }
        }

        bugId = 40040;
        repairId = 40039;
        GrandChildProfession gp40040 = grandChildProfessionMap.get(bugId);
        if(null != gp40040){
            PreparedStatement call = null;
            DBManager db = DatabaseMgr.getDbManager();
            try {
                db.setConnName(DbNameStrategy.getUserDb());
                call = db.getConnection().prepareStatement(sql);
                call.setLong(1, player.getUserId());
                call.setInt(2, bugId);
                if(call.executeUpdate() > 0){//删除成功
                    grandChildProfessionMap.remove(bugId);          //从内存移除
                    addGrandChildProfession(repairId, (int) gp40040.getGrandChildCount());
                }
            } catch (Exception e) {
                log.error(Thread.currentThread().getStackTrace()[1].getMethodName() + ":", e);
            } finally {
                db.closeConnection(call, null);
            }
        }

        bugId = 50040;
        repairId = 50039;
        GrandChildProfession gp50040 = grandChildProfessionMap.get(bugId);
        if(null != gp50040){
            PreparedStatement call = null;
            DBManager db = DatabaseMgr.getDbManager();
            try {
                db.setConnName(DbNameStrategy.getUserDb());
                call = db.getConnection().prepareStatement(sql);
                call.setLong(1, player.getUserId());
                call.setInt(2, bugId);
                if(call.executeUpdate() > 0){//删除成功
                    grandChildProfessionMap.remove(bugId);          //从内存移除
                    addGrandChildProfession(repairId, (int) gp50040.getGrandChildCount());
                }
            } catch (Exception e) {
                log.error(Thread.currentThread().getStackTrace()[1].getMethodName() + ":", e);
            } finally {
                db.closeConnection(call, null);
            }
        }
        //==================================修复代码==========================

        topGrandChild = initTopGrandChildData();
        talentBookMap = new GrandChildTalentBookDaoImpl().getTalentBookMap(getUserId());
        return true;
    }

    @Override
    public boolean afterLoadData() {
        return true;
    }

    @Override
    public boolean saveData() {
        for (GrandChildData data : new ArrayList<>(grandChildDataMap.values())) {
            if (data.isInsertOption()) {
                new GrandChildDataDaoImpl().add(data);
            } else if (data.isUpdateOption()) {
                new GrandChildDataDaoImpl().update(data);
            }
        }
        for (List<SchoolSiteData> siteDataList : new ArrayList<>(schoolSiteMap.values())) {
            for (SchoolSiteData data : new ArrayList<>(siteDataList)) {
                if (data.isInsertOption()) {
                    new SchoolSiteDaoImpl().add(data);
                } else if (data.isUpdateOption()) {
                    new SchoolSiteDaoImpl().update(data);
                }

            }
        }
        List<GrandChildData> syncList = getArchaeusChangeList();
        if (!syncList.isEmpty()) {
            syncGrandChildMsg(syncList);
        }

        for (GrandChildProfession data : grandChildProfessionMap.values()) {
            if (data.isInsertOption()) {
                GrandChildBussiness.addGrandChildProfession(data);
            } else if (data.isUpdateOption()) {
                GrandChildBussiness.updateGrandChildProfession(data);
            }
        }
        for (GrandChildOfficial data : grandChildOfficialMap.values()) {
            if (data.isInsertOption()) {
                GrandChildBussiness.addGrandChildOfficial(data);
            } else if (data.isUpdateOption()) {
                GrandChildBussiness.updateGrandChildOfficial(data);
            }
        }

        for (Map<Integer, PupilData> dataMap : new ArrayList<>(patronsPupilMap.values())) {
            for (PupilData data : new ArrayList<>(dataMap.values())) {
                if (data.isInsertOption()) {
                    new PupiDataDaoImpl().add(data);
                } else if (data.isUpdateOption()) {
                    new PupiDataDaoImpl().update(data);
                }
            }
        }

        // 保存变更的联姻记录
        saveMarriageRecord();

        if (talentBookMap != null) {
            for (GrandChildTalentBook book : new ArrayList<>(talentBookMap.values())) {
                if (book.isInsertOption()) {
                    new GrandChildTalentBookDaoImpl().add(book);
                }
            }
        }

        return true;
    }

    public void saveMarriageRecord() {
        List<MarriageRecord> tempList;
        synchronized (needUpdateMarriageRecordList) {
            tempList = new ArrayList<>(needUpdateMarriageRecordList);
            needUpdateMarriageRecordList.clear();
        }
        for (MarriageRecord record : tempList) {
            MarriageBusiness.updateMarriageRecord(record);
        }
    }

    /**
     * 获取最赚钱孙子
     * @return
     */
    private GrandChildData initTopGrandChildData(){
        GrandChildData tempGrandChild = null;
        if(null != grandChildProfessionTopMap) {
            for (GrandChildData data : grandChildProfessionTopMap.values()) {
                if(null == tempGrandChild){
                    tempGrandChild = data;
                }else {
                    if(data.getEarnSpeed().compareTo(tempGrandChild.getEarnSpeed()) > 0){
                        tempGrandChild = data;
                    }
                }
            }
        }
        return tempGrandChild;
    }

    private List<GrandChildData> getArchaeusChangeList() {
        List<GrandChildData> syncList = new ArrayList<>();
        for (List<SchoolSiteData> siteDataList : new ArrayList<>(schoolSiteMap.values())) {
            for (SchoolSiteData data : new ArrayList<>(siteDataList)) {
                if (StringUtils.isNullOrEmpty(data.getChildId())) {
                    continue;
                }
                GrandChildData childData = grandChildDataMap.get(data.getChildId());
                if (childData == null) {
                    continue;
                }
                boolean isChange = GrandChildConfigMgr.ReCalcArchaeus(childData);
                if (isChange) {
                    syncList.add(childData);
                }
            }
        }
        return syncList;
    }

    @Override
    public void loginSendMsg() {
        //先算下元气值
        getArchaeusChangeList();
        checkSchoolSite();
        syncGrandChildMsgLogin(grandChildDataMap.values());
        checkExamReward();
        GrandChildMgr.syncNoReadExamNo(player);
        //下发科举职业数据
        syncGrandChildProfessionList(new ArrayList<>(grandChildProfessionMap.values()), true);
    }

    @Override
    public void afterLogin() {
        // 跨服拿科举配置
        crossGetExamConfig();
        //下发科举数据
        syncGrandChildOfficialList(new ArrayList<>(grandChildOfficialMap.values()), true);
        checkSchoolSite();
    }

    private void crossGetExamConfig() {
        GrandChildProto.CrossGetExamConfigReqMsg.Builder builder = GrandChildProto.CrossGetExamConfigReqMsg.newBuilder();
        player.sendPacket(CrossProtocol.C_GRAND_CHILD_GET_EXAM_CONFIG, builder);
    }

    private void syncSchoolSite(Collection<List<SchoolSiteData>> list) {
        if (list == null || list.size() <= 0) {
            return;
        }
        GrandChildProto.SyncSchoolSiteMsg.Builder syncMsg = GrandChildProto.SyncSchoolSiteMsg.newBuilder();
        for (List<SchoolSiteData> dataList : new ArrayList<>(list)) {
            for (SchoolSiteData data : new ArrayList<>(dataList)) {
                GrandChildProto.SchoolSiteMsg.Builder msg = GrandChildProto.SchoolSiteMsg.newBuilder();
                msg.setType(data.getSchoolType());
                msg.setPos(data.getPos());
                if (!StringUtils.isNullOrEmpty(data.getChildId())) {
                    GrandChildData childData = grandChildDataMap.get(data.getChildId());
                    if (childData != null) {
                        GrandChildProto.GrandChildMsg.Builder childMsg = GrandChildPb.parseGrandChildMsg(childData);
                        msg.setGrandChild(childMsg);
                    }
                }
                syncMsg.addData(msg);
            }
        }
        player.sendPacket(ClientProtocol.U_SYNC_GRAND_CHILD_SCHOOL_DATA, syncMsg);
    }

    private void checkSchoolSite() {
        if (SystemOpenMgr.systemOpen(player, eSystemId.GrandChild.getValue())) {
            for (eOccupationType occupationType : eOccupationType.values()) {
                int type = (int) occupationType.getValue();
                List<SchoolSiteData> siteDataList = schoolSiteMap.computeIfAbsent(type, k -> new ArrayList<>());
                int num = siteDataList.size();
                while (GrandChildConfigMgr.isFreeSite(num)) {
                    addNewSite(type);
                    num = siteDataList.size();
                }
            }
        }
        for (List<SchoolSiteData> dataList : schoolSiteMap.values()) {
            for (SchoolSiteData data : dataList) {
                if (!StringUtils.isNullOrEmpty(data.getChildId())) {
                    GrandChildData grandChild = getGrandChild(data.getChildId());
                    if (grandChild == null) {
                        data.setChildId("");
                    }
                }
            }
        }
        syncSchoolSite(schoolSiteMap.values());
    }

    private void syncGrandChildMsg(Collection<GrandChildData> grandChildDataList) {
        if (grandChildDataList == null || grandChildDataList.size() <= 0) {
            return;
        }
        GrandChildProto.SyncGrandChildMsg.Builder syncMsg = GrandChildProto.SyncGrandChildMsg.newBuilder();
        for (GrandChildData data : new ArrayList<>(grandChildDataList)) {
            if (data.getLv() == 0) {
                data.setLv(1);
            }
            GrandChildProto.GrandChildMsg.Builder msg = GrandChildPb.parseGrandChildMsg(data);
            syncMsg.addData(msg);
        }
        player.sendPacket(ClientProtocol.U_SYNC_GRANDSON_DATA, syncMsg);
    }

    private void syncGrandChildMsgLogin(Collection<GrandChildData> grandChildDataList) {
        if (grandChildDataList == null) {
            return;
        }
        GrandChildProto.SyncGrandChildMsg.Builder syncMsg = GrandChildProto.SyncGrandChildMsg.newBuilder();
        for (GrandChildData grandChildData : grandChildDataList) {
            if (grandChildData.getLv() == 0) {
                grandChildData.setLv(1);
            }
            GrandChildProto.GrandChildMsg.Builder msg = GrandChildPb.parseGrandChildMsg(grandChildData);
            syncMsg.addData(msg);
        }
        player.sendPacket(ClientProtocol.U_SYNC_GRANDSON_DATA, syncMsg);
    }


    public GrandChildData getGrandChild(String id) {
        GrandChildData childData = grandChildDataMap.get(id);
        return childData;
    }

    public int openSchoolSite(int type) {
        //检查类型

        //检查数量上限
        int siteNum = getSchoolSiteNum(type);
        if (siteNum >= GrandChildConfigMgr.getSchoolMaxSite()) {
            return GameErrorCode.E_GRAND_CHILD_SCHOOL_SITE_LIMIT;
        }
        //检查消耗
        Property consumer = GrandChildConfigMgr.getOpenSiteConsumer(siteNum);
        if (!player.getModule(CurrencyModule.class).removeCurrency(consumer, eLogMoneyType.GrandChild, eLogMoneyType.OpenSchoolSite)) {
            return GameErrorCode.E_BAG_INGOTS_NO_ENOUGH;
        }
        //开坑位
        SchoolSiteData data = addNewSite(type);
        GrandChildProto.BuildSchoolSiteRespMsg.Builder resp = GrandChildProto.BuildSchoolSiteRespMsg.newBuilder();
        resp.setRet(0);
        GrandChildProto.SchoolSiteMsg.Builder siteMsg = GrandChildPb.parseSchoolSiteMsg(data, null);
        resp.setData(siteMsg);
        player.sendPacket(ClientProtocol.U_OPEN_SCHOOL_SITE, resp);

        // 日志
        AutoLogMgr.add(new LogGrandChildOpenSchoolSite(
                getUserId(),
                type,
                siteNum,
                siteNum + 1
        ));

        //埋点
        //GrandChildLog.academyExtend(player, type, siteNum);

        return 0;
    }

    private SchoolSiteData addNewSite(int type) {
        List<SchoolSiteData> siteDataList = schoolSiteMap.get(type);
        if (siteDataList == null) {
            siteDataList = new ArrayList<>();
            schoolSiteMap.put(type, siteDataList);
        }
        SchoolSiteData data = new SchoolSiteData();
        data.setUserId(getUserId());
        data.setSchoolType(type);
        data.setChildId("");
        data.setPos(siteDataList.size());
        data.setInsertOption();
        siteDataList.add(data);
        return data;
    }

    private int getSchoolSiteNum(int type) {
        List<SchoolSiteData> schoolSiteDataList = getSiteDataList(type);
        if (schoolSiteDataList == null) {
            return 0;
        }
        return schoolSiteDataList.size();
    }

    public int enterSchool(int schoolType, List<String> childList, int patronsId) {
        int ret = checkCanEnterSchool(schoolType, childList, patronsId);
        if (ret != 0) {
            return ret;
        }
        this.doEnterSchool(schoolType, childList, patronsId);
        return 0;
    }

    /**
     * 孙子入学
     * @param schoolType
     * @param childList
     * @param patronsId
     * @return
     */
    public List<SchoolSiteData> doEnterSchool(int schoolType, List<String> childList, int patronsId) {
        //下面进行入学操作
        List<SchoolSiteData> siteDataList = new ArrayList<>();
        for (String id : childList) {
            GrandChildData childData = grandChildDataMap.get(id);
            SchoolSiteData school = enterSchool(childData, patronsId, schoolType);
            siteDataList.add(school);
        }
        GrandChildProto.EnterSchoolRespMsg.Builder resp = GrandChildProto.EnterSchoolRespMsg.newBuilder();
        for (SchoolSiteData data : siteDataList) {
            GrandChildProto.SchoolSiteMsg.Builder siteMsg = GrandChildPb.parseSchoolSiteMsg(data, grandChildDataMap.get(data.getChildId()));
            resp.addData(siteMsg);
        }
        resp.setRet(0);
        player.sendPacket(ClientProtocol.U_ENTER_SCHOOL, resp);
        player.getModule(PatronsModule.class).checkPatronsTeacherSkill(patronsId);

        //埋点
//        for (int i = 0; i < childList.size(); i++) {
//            GrandChildData childData = grandChildDataMap.get(childList.get(i));
//            GrandChildLog.academySchoolEntry(player, childData, schoolType);
//        }
        return siteDataList;
    }

    public SchoolSiteData enterSchool(GrandChildData childData, int patronsId, int schoolType) {
        childData.setPatronsId(patronsId);
        childData.setArchaeus(GrandChildConfigMgr.getInitArchaeus());
        childData.setArchaeusLastRecoverTime(System.currentTimeMillis());
        SchoolSiteData site = getSchoolEmptySite(schoolType);
        site.setChildId(childData.getId());
        player.getModule(PatronsModule.class).addGrandChildSpeed(patronsId, childData.getEarnSpeed());
        return site;
    }

    private void addPupilTotalNum(int patronsId) {
        Map<Integer, PupilData> dataMap = getPupilDataMap(patronsId);
        PupilData pupilData1 = dataMap.get(-1);
        if (pupilData1 == null) {
            pupilData1 = new PupilData();
            pupilData1.setUserId(getUserId());
            pupilData1.setPatronsId(patronsId);
            pupilData1.setTitle(-1);
            pupilData1.setInsertOption();
            dataMap.put(-1, pupilData1);
        }
        pupilData1.setNum(pupilData1.getNum() + 1);
    }

    private SchoolSiteData getSchoolEmptySite(int schoolType) {
        List<SchoolSiteData> siteDataList = getSiteDataList(schoolType);
        for (SchoolSiteData data : siteDataList) {
            if (StringUtils.isNullOrEmpty(data.getChildId())) {
                return data;
            }
        }
        return null;
    }

    private int checkCanEnterSchool(int schoolType, List<String> childList, int patronsId) {
        //检查孙子
        if (new HashSet<>(childList).size() != childList.size()) {
            // 传上来的孙子有重复的
            return GameErrorCode.E_GRAND_CHILD_NO_EXIST;
        }
        List<GrandChildData> childDataList = new ArrayList<>();
        for (String id : childList) {
            GrandChildData childData = grandChildDataMap.get(id);
            if (childData == null) {
                return GameErrorCode.E_GRAND_CHILD_NO_EXIST;
            }
            if (childData.getPatronsId() != 0) {
                return GameErrorCode.E_GRAND_CHILD_ENTER_SCHOOL_ALREADY;
            }
            if (childData.getOptionalList().size() != 0) {
                return GameErrorCode.E_GRAND_CHILD_DATA_NO_CONFIRM;
            }
            childDataList.add(childData);
        }
        //检查类型是否一致
        for (GrandChildData data : childDataList) {
            int occupation = data.getOccupation();
            if (schoolType != occupation) {
                return GameErrorCode.E_GRAND_CHILD_SCHOOL_TYPE_NO_MATCH;
            }
        }
        //检查门客
        UserPatrons userPatrons = player.getModule(PatronsModule.class).getUserPatrons(patronsId);
        if (userPatrons == null) {
            return GameErrorCode.E_PATRONS_NO_FOUND;
        }
        if (PatronsMgr.getPatronsInfo(patronsId).getOccupation() != schoolType) {
            //门客职业与书院不符
            return GameErrorCode.E_GRAND_CHILD_SCHOOL_PATRONS_TYPE_NO_MATCH;
        }
        //检查坑位是否足够
        int emptySiteNum = getSchoolEmptySiteNum(schoolType);
        if (emptySiteNum < childDataList.size()) {
            return GameErrorCode.E_GRAND_CHILD_SCHOOL_FULL;
        }
        return 0;
    }

    private int getSchoolEmptySiteNum(int type) {
        int num = 0;
        List<SchoolSiteData> schoolSiteDataList = getSiteDataList(type);
        if (schoolSiteDataList == null) {
            return num;
        }
        for (SchoolSiteData data : schoolSiteDataList) {
            if (StringUtils.isNullOrEmpty(data.getChildId())) {
                num++;
            }
        }
        return num;
    }

    public int train(int schoolType) {
        GrandChildProto.SchoolTrainRespMsg.Builder resp = GrandChildProto.SchoolTrainRespMsg.newBuilder();
        List<SchoolSiteData> siteDataList = getSiteDataList(schoolType);
        if (siteDataList != null) {
            for (SchoolSiteData data : siteDataList) {
                if (StringUtils.isNullOrEmpty(data.getChildId())) {
                    continue;
                }
                doTrainResult(resp, data, 0);
            }
        }
        if (resp.getResultList().size() <= 0) {
            //没有培养 说明元气不足
            resp.setRet(GameErrorCode.E_GRAND_CHILD_SCHOOL_ARCHAEUS_NO_ENOUGH);
        } else {
            resp.setRet(0);
        }
        player.sendPacket(ClientProtocol.U_GRAND_CHILD_TRAIN, resp);
        return 0;
    }

    public void doTrainResult(GrandChildProto.SchoolTrainRespMsg.Builder resp, SchoolSiteData data, int useNum) {
        GrandChildProto.SchoolTrainResultMsg.Builder resultMsg = GrandChildProto.SchoolTrainResultMsg.newBuilder();
        resultMsg.setType(data.getSchoolType());
        resultMsg.setPos(data.getPos());
        GrandChildData childData = grandChildDataMap.get(data.getChildId());
        if (childData == null) {
            return;
        }
        if (childData.getLv() == childData.getMaxLv()) {
            return;
        }

        // 日志用
        int logOldLv = childData.getLv();
        List<Integer> logOldFlairList = new ArrayList<>(childData.getFlairList());
        BigInteger logOldEarnSpeed = childData.getEarnSpeed();
        boolean logGraduate = false;
        //重新计算一下元气值
        GrandChildConfigMgr.ReCalcArchaeus(childData);
        if (useNum == 0) {
            int archaeus = childData.getArchaeus();
            if (archaeus < useNum) {
                player.notifyListener(eGamePlayerEventType.RechargeGrandChildNew.getValue(), 0);
                return;
            }
            useNum = archaeus;
        }
        if (useNum <= 0) {
            //没有元气
            player.notifyListener(eGamePlayerEventType.RechargeGrandChildNew.getValue(), 0);
            return;
        }


        int needArchaeus = GrandChildConfigMgr.grandChildNextLvNeedExp(childData);
        if (needArchaeus <= 0 || useNum < needArchaeus) {
            //不可能 先返回
            return;
        }
        //只有能升级 才消耗
        int reduceArchaeus = 0;
        while (useNum >= needArchaeus) {
            useNum -= needArchaeus;
            childData.setArchaeus(childData.getArchaeus() - needArchaeus);
            childData.setTrainExp(childData.getTrainExp() + needArchaeus);
            reduceArchaeus += needArchaeus;
            checkChildUpgrade(childData, resultMsg);
            if (canGraduation(childData)) {
                logGraduate = true;
                //毕业了
                GraduationResult graduationResult = graduation(data, childData);
                GrandChildProto.GraduationMsg.Builder builder = GrandChildProto.GraduationMsg.newBuilder();
                builder.setNewJob(graduationResult.isNewJob());
                builder.setAddTeacherExp(graduationResult.getAddTeacherExp());
                builder.setAddBaiYeExp(graduationResult.getAddBaiYeExp());
                builder.setGrandChild(GrandChildPb.parseGrandChildMsg(childData));
                resultMsg.setGraduation(builder);
                break;
            }
            needArchaeus = GrandChildConfigMgr.grandChildNextLvNeedExp(childData);
        }

        resultMsg.setGrandChild(GrandChildPb.parseGrandChildMsg(childData));
        syncGrandChildMsg(Collections.singletonList(childData));
        resp.addResult(resultMsg);

        player.notifyListener(eGamePlayerEventType.GrandChildTrainTimes.getValue(),reduceArchaeus);

        // 日志
        AutoLogMgr.add(new LogGrandChildTrain(
                getUserId(),
                childData.getId(),
                data.getSchoolType(),
                data.getPos(),
                useNum,
                logOldLv,
                logOldFlairList,
                logOldEarnSpeed.toString(),
                childData.getLv(),
                childData.getFlairList(),
                childData.getEarnSpeed().toString(),
                logGraduate ? 1 : 0
        ));

        //埋点
        if(logGraduate) {
            //GrandChildLog.academySchoolGraduate(player, childData, data);
        }

        if (childData.getLv() < childData.getMaxLv()) {
            if(childData.getArchaeus() <= 0){
                player.notifyListener(eGamePlayerEventType.RechargeGrandChildNew.getValue(), 0);
            }
        }
    }

    private boolean canGraduation(GrandChildData childData) {
        return childData.getLv() >= childData.getMaxLv();
    }

    private void checkChildUpgrade(GrandChildData childData, GrandChildProto.SchoolTrainResultMsg.Builder resultMsg) {
        int quality = childData.getQuality();
        int skillType = GrandChildConfigMgr.getSkillUpgradeType(quality);
        SkillUpgradeInfo upgradeInfo = SkillMgr.getSkillUpgradeInfo(skillType, childData.getLv());
        if (upgradeInfo == null || upgradeInfo.getConsumeCount() == 0) {
            //没有配置或者 满级了
            return;
        }
        if (childData.getTrainExp() >= upgradeInfo.getConsumeCount()) {
            //经验足够 升级
            //先扣经验
            childData.setTrainExp((int) (childData.getTrainExp() - upgradeInfo.getConsumeCount()));
            childData.setLv(childData.getLv() + 1);
            //加赚速
            GrandChildUpgradeResult grandChildUpgradeResult = GrandChildConfigMgr.calcAddEarnSpeed(player, childData, false);
            BigInteger addEarnSpeed = grandChildUpgradeResult.getAddEarnSpeed();
            // 只能加增加的赚速
            player.getModule(PatronsModule.class).addGrandChildSpeed(childData.getPatronsId(), addEarnSpeed);
            childData.setEarnSpeed(childData.getEarnSpeed().add(addEarnSpeed));
            GrandChildProto.TrainEffectMsg.Builder effectMsg = GrandChildProto.TrainEffectMsg.newBuilder();
            effectMsg.setValue(addEarnSpeed.toString());
            effectMsg.addAllTalentType(grandChildUpgradeResult.getEffectTalentList());
            resultMsg.addEffect(effectMsg);
            //孙子赚速冲榜
            player.notifyListener(eGamePlayerEventType.GrandChildEarnSpeedRiseRank.getValue(), addEarnSpeed);
//            player.notifyListener(eGamePlayerEventType.CrossGrandChildEarnSpeedRiseRank.getValue(), addEarnSpeed);
        }
    }

    private List<SchoolSiteData> getSiteDataList(int schoolType) {
        if (schoolType != 0) {
            return schoolSiteMap.get(schoolType);
        }
        List<SchoolSiteData> tempList = new ArrayList<>();
        for (List<SchoolSiteData> list : schoolSiteMap.values()) {
            tempList.addAll(list);
        }
        return tempList;
    }

    /**
     * 孙子毕业
     *
     * @param data
     * @param childData
     */
    private GraduationResult graduation(SchoolSiteData data, GrandChildData childData) {
        //给孙子随机一个工作职业
        int grandChildProfessionId = ConfigMgr.getGrandChildProfessionId(childData.getOccupation());
        childData.setProfessionId(grandChildProfessionId);
        boolean isNewJob = isNewJob(grandChildProfessionId);

        List<TalentTriggerResult> triggerTalentResult = TalentUtils.getTriggerTalentResult(childData, eGrandChildSkillEffectStage.GRADUATION);
        TalentResultUtils.applyTalentAttrChange(childData, triggerTalentResult);
        int teachExpAddition = TalentResultUtils.getTeachExpAddition(childData, triggerTalentResult);
        int baiYeExpAddition = TalentResultUtils.getBaiYeExpAddition(childData, triggerTalentResult);


        //添加名师点
        int teacherExp = GrandChildConfigMgr.getGraduationAddTeacherExp(childData,teachExpAddition);
        player.getModule(PatronsModule.class).addTeachExp(childData.getPatronsId(), teacherExp);
        //添加百叶经验
        int baiYeExp = GrandChildConfigMgr.getGraduationAddBaiYeExp(childData,baiYeExpAddition);
        player.getModule(PatronsModule.class).addBaiYeExp(childData.getPatronsId(), baiYeExp);
        data.setChildId("");
        addPupilTotalNum(childData.getPatronsId());
        //比较是否最赚钱孙子
        compareProfessionGrandChild(childData);
        //增加孙子职业数据
        addGrandChildProfession(grandChildProfessionId, 1);
        //排行榜变更
        grandChildRankChange(childData);
        return new GraduationResult(isNewJob, teacherExp, baiYeExp);
    }

    private boolean isNewJob(int grandChildProfessionId) {
        GrandChildProfession profession = grandChildProfessionMap.get(grandChildProfessionId);
        if (profession == null || profession.getGrandChildCount() == 0) {
            return true;
        }
        return false;
    }

    public int quickTrain(int schoolType, int pos, int useNum) {
        SchoolSiteData schoolSite = getSchoolSite(schoolType, pos);
        if (schoolSite == null) {
            return GameErrorCode.E_GRAND_CHILD_NO_EXIST;
        }
        String childId = schoolSite.getChildId();
        GrandChildData childData = grandChildDataMap.get(childId);
        if (childData == null) {
            return GameErrorCode.E_GRAND_CHILD_NO_EXIST;
        }
        if (childData.getMaxLv() <= childData.getLv()) {
            return GameErrorCode.E_GRAND_CHILD_SCHOOL_CAN_GRADUATION;
        }

        GrandChildConfigMgr.ReCalcArchaeus(childData);
        int needArchaeus = GrandChildConfigMgr.grandChildNextLvNeedExp(childData);
        if (childData.getArchaeus() < useNum || useNum < needArchaeus) {
            if (player.getModule(BagModule.class).getUserBagItemCount(GameConfig.GRAND_CHILD_ARCHAEUS_RECOVER_ITEM_ID) <= 0) {
                player.notifyListener(eGamePlayerEventType.RechargeGrandChild.getValue(), 0);
                player.notifyListener(eGamePlayerEventType.RechargeGrandChildNew.getValue(), 0);
            }
            return GameErrorCode.E_GRAND_CHILD_SCHOOL_ARCHAEUS_NO_ENOUGH;
        }
        GrandChildProto.SchoolTrainRespMsg.Builder resp = GrandChildProto.SchoolTrainRespMsg.newBuilder();
        doTrainResult(resp, schoolSite,useNum);
        resp.setRet(0);
        player.sendPacket(ClientProtocol.U_GRAND_CHILD_QUICK_TRAIN, resp);

        if (childData.getMaxLv() > childData.getLv()) {
            if(childData.getArchaeus() <= 0){
                player.notifyListener(eGamePlayerEventType.RechargeGrandChildNew.getValue(), 0);
            }
        }
        return 0;
    }

    private SchoolSiteData getSchoolSite(int schoolType, int pos) {
        List<SchoolSiteData> siteDataList = getSiteDataList(schoolType);
        if (siteDataList == null) {
            return null;
        }
        for (SchoolSiteData data : siteDataList) {
            if (data.getPos() == pos) {
                return data;
            }
        }
        return null;
    }

    private void syncDataToShowList(GrandChildData grandChildData) {
        List<GrandChildData> dataList = getPatronsShowList(grandChildData.getPatronsId());
        dataList.sort((o1, o2) -> {
            //头衔相等
            if (o1.getTitle() == o2.getTitle()) {
                return o2.getEarnSpeed().compareTo(o1.getEarnSpeed());
            }
            //头衔不等 0最小 1最大  特么的
            int title1 = o1.getTitle() == 0 ? 999 : o1.getTitle();
            int title2 = o2.getTitle() == 0 ? 999 : o2.getTitle();
            return title1 - title2;
        });
    }

    public void showPatronsPupil(int patronsId) {
        List<GrandChildData> dataList = getPatronsShowList(patronsId);
        List<GrandChildData> unExamList = getGrandChildByPatronsId(patronsId);
        GrandChildProto.PupilListRespMsg.Builder resp = GrandChildProto.PupilListRespMsg.newBuilder();
        Map<String, GrandChildData> grandChildDataMap = new HashMap<>();
        if (dataList != null) {
            for (GrandChildData data : dataList) {
                grandChildDataMap.put(data.getId(), data);
            }
        }
        if (unExamList != null) {
            for (GrandChildData data : unExamList) {
                grandChildDataMap.put(data.getId(), data);
            }
        }
        ArrayList<GrandChildData> list = new ArrayList<>(grandChildDataMap.values());
        list.sort((o1, o2) -> {
            //头衔相等
            if (o1.getTitle() == o2.getTitle()) {
                return o2.getEarnSpeed().compareTo(o1.getEarnSpeed());
            }
            //头衔不等 0最小 1最大  特么的
            int title1 = o1.getTitle() == 0 ? 999 : o1.getTitle();
            int title2 = o2.getTitle() == 0 ? 999 : o2.getTitle();
            return title1 - title2;
        });
        for (GrandChildData data : list) {
            GrandChildProto.PupilDataMsg.Builder pupilMsg = GrandChildPb.parsePupilShowData(data);
            resp.addData(pupilMsg);
            if (resp.getDataCount() >= 30) {
                break;
            }
        }
        Map<Integer, PupilData> dataMap = patronsPupilMap.get(patronsId);
        resp.setTotalNum(0);
        if (dataMap != null) {
            for (PupilData data : new ArrayList<>(dataMap.values())) {
                if (data.getTitle() == -1) {
                    resp.setTotalNum(data.getNum());
                } else {
                    GrandChildProto.PupilTitleNumMsg.Builder msg = GrandChildProto.PupilTitleNumMsg.newBuilder();
                    msg.setNum(data.getNum());
                    msg.setTitleId(data.getTitle());
                    resp.addTitleNumMsg(msg);
                }
            }
            GrandChildProto.PupilTitleNumMsg.Builder msg = GrandChildProto.PupilTitleNumMsg.newBuilder();
            msg.setNum(getUnJoinExamList(patronsId).size());
            msg.setTitleId(0);
            resp.addTitleNumMsg(msg);
        }
        //加个科举中吧
        GrandChildProto.PupilTitleNumMsg.Builder msg = GrandChildProto.PupilTitleNumMsg.newBuilder();
        msg.setNum(getInExamList(patronsId).size());
        msg.setTitleId(8);
        resp.addTitleNumMsg(msg);
        player.sendPacket(ClientProtocol.U_GRAND_CHILD_PATRONS_PUPIL, resp);
    }

    private List<GrandChildData> getInExamList(int patronsId) {
        List<GrandChildData> list = new ArrayList<>();
        for (GrandChildData data : grandChildDataMap.values()) {
            if (data.getHadExam() == 1 && data.getPatronsId() == patronsId && canGraduation(data) && data.getTitle() == 0) {
                list.add(data);
            }
        }
        return list;
    }

    private List<GrandChildData> getUnJoinExamList(int patronsId) {
        List<GrandChildData> list = new ArrayList<>();
        for (GrandChildData data : grandChildDataMap.values()) {
            if (data.getHadExam() == 0 && data.getPatronsId() == patronsId && canGraduation(data)) {
                list.add(data);
            }
        }
        return list;
    }

    private List<GrandChildData> getPatronsShowList(int patronsId) {
        List<GrandChildData> dataList = patronsShowChild.get(patronsId);
        if (dataList == null) {
            //去查数据库得了
            int size = 30;
            dataList = new GrandChildDataDaoImpl().getPatronsShowChild(getUserId(), patronsId, size);
            patronsShowChild.put(patronsId, dataList);
            for (GrandChildData data : dataList) {
                if (!grandChildDataMap.containsKey(data.getId())) {
                    grandChildDataMap.put(data.getId(), data);
                }
            }
        }
        return dataList;
    }

    private List<GrandChildData> getGrandChildByPatronsId(int patronsId) {
        List<GrandChildData> list = new ArrayList<>();
        for (GrandChildData data : new ArrayList<>(grandChildDataMap.values())) {
            if (data.getPatronsId() == patronsId && canGraduation(data)) {
                list.add(data);
            }
        }
        return list;
    }

    /**
     * 增加孙子职业数量
     *
     * @param professionId
     * @param count
     */
    private void addGrandChildProfession(int professionId, int count) {
        if (count <= 0) {
            return;
        }
        if (!grandChildProfessionMap.containsKey(professionId)) {
            synchronized (grandChildProfessionMap) {
                if (!grandChildProfessionMap.containsKey(professionId)) {
                    GrandChildProfession grandChildProfession = new GrandChildProfession();
                    grandChildProfession.setUserId(player.getUserId());
                    grandChildProfession.setProfessionId(professionId);
                    grandChildProfession.setGrandChildCount(0);
                    grandChildProfession.setSkillLv(0);
                    grandChildProfession.setInsertOption();
                    grandChildProfessionMap.put(professionId, grandChildProfession);
                }
            }
        }
        GrandChildProfession grandChildProfession = grandChildProfessionMap.get(professionId);
        if (null != grandChildProfession) {
            grandChildProfession.setGrandChildCount(grandChildProfession.getGrandChildCount() + count);
            syncGrandChildProfession(grandChildProfession);
        }
    }

    /**
     * 孙子职业技能升级
     * @param professionId
     * @return
     */
    public int grandChildProfessionSkillUpgrade(int professionId) {

        List<GrandChildProfession> changeList = new ArrayList<>();
        if(professionId < 0){
            return GameErrorCode.E_REQUEST_ARGUMENT_ERROR;
        }

        for (Map.Entry<Integer, GrandChildProfession> entry : grandChildProfessionMap.entrySet()) {
            int professionIdKey = entry.getKey();
            GrandChildProfession grandChildProfession = entry.getValue();
            if(professionId > 0 && professionId != professionIdKey){
                continue;
            }
            //获取职业技能信息
            int childOccupation = GrandChildConfigMgr.getChildOccupation(professionIdKey);
            if (0 == childOccupation) {
                continue;
            }
            int professionSkillId = ConfigMgr.getGrandChildProfessionSkill(childOccupation);
            SkillInfo skillInfo = SkillMgr.getSkillInfo(professionSkillId);
            if (null == skillInfo) {
                continue;
            }
            int addLv = 0;
            int loop = 1000;
            for (int k = 0; k < loop; k++) {
                if (grandChildProfession.getSkillLv() + addLv >= skillInfo.getMaxLevel()) {
                    break;
                }
                //升级信息
                int upgradeType = skillInfo.getUpgradeType(0);
                SkillUpgradeInfo nextSkillUpgradeInfo = SkillMgr.getSkillUpgradeInfo(upgradeType, grandChildProfession.getSkillLv() + addLv + 1);
                if (null == nextSkillUpgradeInfo) {
                    break;
                }
                SkillUpgradeInfo skillUpgradeInfo = SkillMgr.getSkillUpgradeInfo(upgradeType, grandChildProfession.getSkillLv() + addLv);
                if (null == skillUpgradeInfo) {
                    break;
                }
                if (grandChildProfession.getGrandChildCount() < skillUpgradeInfo.getConsumeCount()) {
                    break;
                }
                addLv++;
                //解锁
                if(grandChildProfession.getSkillLv() == 0){
                    break;
                }
            }

            if(addLv > 0){
                grandChildProfession.setSkillLv(grandChildProfession.getSkillLv() + addLv);
                changeList.add(grandChildProfession);
                // 日志
                AutoLogMgr.add(new LogGrandChildProfessionSkillUpgrade(
                        getUserId(),
                        skillInfo.getId(),
                        grandChildProfession.getSkillLv() - addLv,
                        grandChildProfession.getSkillLv(),
                        grandChildProfession.getGrandChildCount()
                ));
            }
        }

        if(!changeList.isEmpty()){
            for (GrandChildProfession profession : changeList) {
                //同步数据
                syncGrandChildProfession(profession);
            }
            player.getModule(PatronsModule.class).onPatronsChange(null);
            player.getModule(PatronsModule.class).syncPatronsDataAll(false);
        }

//        GrandChildProfession grandChildProfession = grandChildProfessionMap.get(professionId);
//        if (null == grandChildProfession) {
//            return GameErrorCode.E_GRAND_CHILD_PROFESSION_LOCK;
//        }
//        //获取职业技能信息
//        int childOccupation = GrandChildConfigMgr.getChildOccupation(professionId);
//        if (0 == childOccupation) {
//            return GameErrorCode.E_GRAND_CHILD_PROFESSION_CONFIG_ERROR;
//        }
//        int professionSkillId = ConfigMgr.getGrandChildProfessionSkill(childOccupation);
//        SkillInfo skillInfo = SkillMgr.getSkillInfo(professionSkillId);
//        if (null == skillInfo) {
//            return GameErrorCode.E_GRAND_CHILD_PROFESSION_SKILL_NO_EXIST;
//        }
//        if (grandChildProfession.getSkillLv() >= skillInfo.getMaxLevel()) {
//            return GameErrorCode.E_GRAND_CHILD_PROFESSION_SKILL_MAX_LEVEL;
//        }
//        //升级信息
//        int upgradeType = skillInfo.getUpgradeType(0);
//        SkillUpgradeInfo nextSkillUpgradeInfo = SkillMgr.getSkillUpgradeInfo(upgradeType, grandChildProfession.getSkillLv() + 1);
//        if (null == nextSkillUpgradeInfo) {
//            return GameErrorCode.E_GRAND_CHILD_PROFESSION_SKILL_MAX_LEVEL;
//        }
//        SkillUpgradeInfo skillUpgradeInfo = SkillMgr.getSkillUpgradeInfo(upgradeType, grandChildProfession.getSkillLv());
//        if (null == skillUpgradeInfo) {
//            return GameErrorCode.E_GRAND_CHILD_PROFESSION_SKILL_MAX_LEVEL;
//        }
//        if (grandChildProfession.getGrandChildCount() < skillUpgradeInfo.getConsumeCount()) {
//            return GameErrorCode.E_GRAND_CHILD_PROFESSION_SON_COUNT_NO_ENOUGH;
//        }
//        grandChildProfession.setSkillLv(grandChildProfession.getSkillLv() + 1);
//
//        //同步数据
//        syncGrandChildProfession(grandChildProfession);
//        player.getModule(PatronsModule.class).onPatronsChange(null);
//        player.getModule(PatronsModule.class).syncPatronsDataAll(false);
//
//        // 日志
//        LogMgr.addLogGrandChildProfessionSkillUpgrade(new LogGrandChildProfessionSkillUpgrade(
//                getUserId(),
//                skillInfo.getId(),
//                grandChildProfession.getSkillLv() - 1,
//                grandChildProfession.getSkillLv(),
//                grandChildProfession.getGrandChildCount()
//        ));

        return 0;
    }

    /**
     * 获取孙子职业列表
     *
     * @param professionId
     * @return
     */
    public void sendGrandChildListByProfession(int professionId, int pageNum) {
        Map<Integer, List<GrandChildData>> dataListMap = new ConcurrentHashMap<>();
        GrandChildProto.ProfessionGrandChildListRespMsg.Builder respMsg = GrandChildProto.ProfessionGrandChildListRespMsg.newBuilder();
        boolean needLoadData = false;
        if (pageNum < 0) {
            pageNum = 0;
        }
        if (0 == pageNum) {
            needLoadData = true;
        } else {
            if (!professionGrandChildListMap.containsKey(professionId)) {
                needLoadData = true;
            }
        }
        if (needLoadData) {
            List<GrandChildData> dataList = new GrandChildDataDaoImpl().getProfessionGrandChildList(player.getUserId(), professionId);
            dataListMap.put(professionId, dataList);
            professionGrandChildListMap = dataListMap;
        }
        List<GrandChildData> dataList = professionGrandChildListMap.get(professionId);
        respMsg.setTotalNum(dataList.size() % getPageSize() == 0 ? dataList.size() / getPageSize() : dataList.size() / getPageSize() + 1);
        respMsg.setPageNum(pageNum);
        dataList.sort((o1, o2) -> o2.getEarnSpeed().compareTo(o1.getEarnSpeed()));
        int startIndex = pageNum * getPageSize();
        int stopIndex = Math.min(startIndex + getPageSize(), dataList.size());
        List<GrandChildData> resultList = new ArrayList<>();
        if (startIndex >= dataList.size()) {
            player.sendPacket(Protocol.U_GRAND_CHILD_LIST_BY_PROFESSION, respMsg);
            return;
        }
        for (int i = startIndex; i < stopIndex; i++) {
            resultList.add(dataList.get(i));
        }
        for (GrandChildData data : resultList) {
            GrandChildProto.GrandChildMsg.Builder tempMsg = GrandChildPb.parseGrandChildMsg(data);
            respMsg.addGrandChild(tempMsg);
        }
        player.sendPacket(ClientProtocol.U_GRAND_CHILD_LIST_BY_PROFESSION, respMsg);
    }

    /**
     * 增加孙子科举官职数量
     *
     * @param officialId
     * @param count
     */
    private void addGrandChildOfficial(int officialId, int count) {
        if (!grandChildOfficialMap.containsKey(officialId)) {
            synchronized (grandChildOfficialMap) {
                if (!grandChildOfficialMap.containsKey(officialId)) {
                    GrandChildOfficial grandChildOfficial = new GrandChildOfficial();
                    grandChildOfficial.setUserId(player.getUserId());
                    grandChildOfficial.setOfficialId(officialId);
                    grandChildOfficial.setGrandChildCount(0);
                    grandChildOfficial.setInsertOption();
                    grandChildOfficialMap.put(officialId, grandChildOfficial);
                }
            }
        }
        GrandChildOfficial grandChildOfficial = grandChildOfficialMap.get(officialId);
        if (null != grandChildOfficial) {
            grandChildOfficial.setGrandChildCount(grandChildOfficial.getGrandChildCount() + count);
            //同步科举数据
            syncGrandChildOfficial(grandChildOfficial);
            //科举成就
            player.notifyListener(eGamePlayerEventType.GrandChildOfficialCount.getValue(), grandChildOfficial);
        }
    }

    /**
     * 同步科举职业数据
     *
     * @param grandChildProfession
     */
    private void syncGrandChildProfession(GrandChildProfession grandChildProfession) {
        if (null != grandChildProfession) {
            List<GrandChildProfession> syncList = new ArrayList<>();
            syncList.add(grandChildProfession);
            syncGrandChildProfessionList(syncList, false);
        }
    }

    /**
     * 同步科举职业数据
     *
     * @param grandChildProfessionList
     * @param isLogin
     */
    private void syncGrandChildProfessionList(List<GrandChildProfession> grandChildProfessionList, boolean isLogin) {
        if (isLogin) {
            player.sendPacket(Protocol.U_GRAND_CHILD_PROFESSION_LIST,
                    GrandChildPb.parseFamilyTreeProfessionListMsg(grandChildProfessionList, grandChildProfessionTopMap));
        } else {
            player.sendPacket(Protocol.U_GRAND_CHILD_PROFESSION_SYNC,
                    GrandChildPb.parseFamilyTreeProfessionListMsg(grandChildProfessionList, grandChildProfessionTopMap));
        }
    }

    /**
     * 同步科举数据
     *
     * @param grandChildOfficial
     */
    private void syncGrandChildOfficial(GrandChildOfficial grandChildOfficial) {
        if (null != grandChildOfficial) {
            List<GrandChildOfficial> syncList = new ArrayList<>();
            syncList.add(grandChildOfficial);
            syncGrandChildOfficialList(syncList, false);
        }
    }

    /**
     * 同步科举数据
     *
     * @param grandChildOfficialList
     * @param isLogin
     */
    private void syncGrandChildOfficialList(List<GrandChildOfficial> grandChildOfficialList, boolean isLogin) {
        if (isLogin) {
            player.sendPacket(Protocol.U_GRAND_CHILD_OFFICIAL_LIST, GrandChildPb.parseFamilyTreeOfficialListMsg(grandChildOfficialList));
        } else {
            player.sendPacket(Protocol.U_GRAND_CHILD_OFFICIAL_SYNC, GrandChildPb.parseFamilyTreeOfficialListMsg(grandChildOfficialList));
        }
    }

    // ============================  科举考试  ==============================

    /**
     * 参加科举考试
     */
    public int joinExam(String childId, String examNo) {
        GrandChildData childData = grandChildDataMap.get(childId);
        if (childData == null) {
            return GameErrorCode.E_GRAND_CHILD_NO_EXIST;
        }
        if (childData.getHadExam() != 0) {
            return GameErrorCode.E_GRAND_CHILD_EXAM_HAD_JOIN;
        }
        long now = System.currentTimeMillis();
        if (now - childData.getReqJoinExamTime() < 1000) {
            return GameErrorCode.E_GRAND_CHILD_EXAM_JOIN_FREQUENTLY;
        }

        if (childData.getProfessionId() == 0 || childData.getLv() < childData.getMaxLv()) {
            return GameErrorCode.E_GRAND_CHILD_EXAM_JOIN_FREQUENTLY;
        }

        // 天赋加成
        List<TalentTriggerResult> triggerTalentResult = TalentUtils.getTriggerTalentResult(childData, eGrandChildSkillEffectStage.EXAM);
        int addition = TalentResultUtils.getExamWeightAddition(childData, triggerTalentResult);

        // 设置请求时间
        childData.setReqJoinExamTime(now);
        // 发跨服
        GrandChildProto.CrossJoinExamReqMsg.Builder builder = GrandChildProto.CrossJoinExamReqMsg.newBuilder();
        builder.setId(childData.getId());
        builder.setNo(examNo);
        builder.setAppearanceId(childData.getAppearanceId());
        builder.setUserBaseInfo(PlayerPb.parsePlayerBaseTempMsg(player.getUserInfo()));
        builder.setName(childData.getNickName());
        builder.setEarnSpeed(childData.getEarnSpeed().toString());
        builder.setBuff(String.valueOf(addition));
        player.sendPacket(CrossProtocol.C_GRAND_CHILD_JOIN_IMPERIAL_EXAM, builder);
        return 0;
    }

    /**
     * 参加科举考试  跨服返回
     */
    public void joinExamFromCross(int ret, String childId, long examNo) {
        GrandChildData grandChildData = grandChildDataMap.get(childId);

        if (ret != 0) {
            // 参加失败
            grandChildData.setReqJoinExamTime(0);
        } else {
            // 参加成功
            if (grandChildData.getHadExam() == 0) {
                grandChildData.setHadExam(1);
            } else {
                log.error("repeated join exam! userId = {}, childI = {}", player.getUserId(), childId);
            }

            // 日志
            List<TalentTriggerResult> result = TalentUtils.getTriggerTalentResult(grandChildData, eGrandChildSkillEffectStage.EXAM);
            int examWeightAddition = TalentResultUtils.getExamWeightAddition(grandChildData, result);
            AutoLogMgr.add(new LogGrandChildExamResult(
                    getUserId(),
                    childId,
                    1,
                    examNo,
                    grandChildData.getEarnSpeed().toString(),
                    examWeightAddition,
                    grandChildData.getTitle()
            ));

        }

        // 同步孙子给玩家
        syncGrandChildMsg(Collections.singletonList(grandChildData));
    }

    /**
     * 请求科举结果  跨服返回
     */
    public void getExamResultFromCross(GrandChildProto.ExamResultRewardRespMsg.Builder builder) {

        // 科举结果才展示奖励
        if (builder.getType() == 1 && builder.getResultCount() > 0) {

            Property goodsReward = new Property();
            Property teachReward = new Property(); // 这里goodsId是门客ID

            List<String> rewardList = StringUtils.stringToStringList(GameConfig.GRAND_CHILD_EXAM_REWARD, "\\|").stream().map(item -> item.split(",")[1]).collect(Collectors.toList());
            for (GrandChildProto.ExamPupilResultMsg resultMsg : builder.getResultList()) {

                // 不显示别人的
                if (resultMsg.getUserId() != player.getUserId()) {
                    continue;
                }

                String id = resultMsg.getId();
                int title = resultMsg.getTitle();

                GrandChildData grandChildData = GrandChildMgr.getGrandChildData(getUserId(), id);
                if (grandChildData == null) {
                    continue;
                }
                Property reward = PropertyHelper.parseStringToProperty(rewardList.get(title - 1));

                int patronsId = grandChildData.getPatronsId();

                // 添加授学经验奖励
                if (patronsId > 0) {
                    teachReward.addProperty(patronsId, reward.getCountByGoodsId(GameConfig.GRAND_CHILD_TEACH_ITEM_ID));
                }
                reward.removeProperty(GameConfig.GRAND_CHILD_TEACH_ITEM_ID);
                // 添加道具奖励
                goodsReward.addProperty(reward);
            }

            builder.setReward(PropertyHelper.parsePropertyToString(goodsReward));
            builder.setPatronsReward(PropertyHelper.parsePropertyToString(teachReward));
        }

        player.sendPacket(ClientProtocol.U_GRAND_CHILD_GET_IMPERIAL_EXAM_RESULT, builder);
    }

    /**
     * 检查未领取的科举奖励
     */
    public void checkExamReward() {
        List<GrandChildExamResult> resultList = GrandChildMgr.getExamResultAndRemove(player);
        try {
            addExamReward(resultList);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    /**
     * 添加科举结果奖励
     *
     * @param resultList 多场科举结果
     */
    private void addExamReward(List<GrandChildExamResult> resultList) {
        List<GrandChildData> syncList = new ArrayList<>();
        for (GrandChildExamResult result : resultList) {

            // 取奖励
            GrandChildExamReward reward = calGrandChildExamReward(result.getMap());

            // 设置title
            for (Map.Entry<String, Integer> entry : result.getMap().entrySet()) {
                String childId = entry.getKey();
                int title = entry.getValue();
                GrandChildData grandChildData = grandChildDataMap.get(childId);
                if (grandChildData != null) {
                    grandChildData.setTitle(title);
                    syncList.add(grandChildData);
                    addPatronsPupil(grandChildData);
                    //同步到展示列表去
                    syncDataToShowList(grandChildData);
                } else {
                    log.info("examNo = {}, id = {}, set title = {} fail!", result.getNo(), childId, title);
                }
            }

            // 加门客经验
            for (Map.Entry<Integer, BigInteger> entry : reward.getTeachReward().getGoods().entrySet()) {
                int patronsId = entry.getKey();
                long exp = entry.getValue().longValue();
                player.getModule(PatronsModule.class).addTeachExp(patronsId, exp);
            }

            // 加道具奖励
            player.getModule(CurrencyModule.class).addCurrency(reward.getGoodsReward(), eLogMoneyType.GrandChild, eLogMoneyType.GrandChildExamReward);

            // 记录科举数据
            Map<Integer, Integer> titleCountMap = new ConcurrentHashMap<>();
            for (int title : result.getMap().values()) {
                titleCountMap.put(title, titleCountMap.getOrDefault(title, 0) + 1);
            }
            for (Map.Entry<Integer, Integer> entry : titleCountMap.entrySet()) {
                addGrandChildOfficial(entry.getKey(), entry.getValue());
            }

            // 日志
            try {
                for (Map.Entry<String, Integer> entry : result.getMap().entrySet()) {
                    String earnSpeed = "-1";
                    int addition = -1;
                    GrandChildData data = grandChildDataMap.get(entry.getKey());
                    if (data != null) {
                        earnSpeed = data.getEarnSpeed().toString();
                        List<TalentTriggerResult> list = TalentUtils.getTriggerTalentResult(data, eGrandChildSkillEffectStage.EXAM);
                        addition = TalentResultUtils.getExamWeightAddition(data, list);
                    }

                    AutoLogMgr.add(new LogGrandChildExamResult(
                            getUserId(),
                            entry.getKey(),
                            2,
                            Long.valueOf(result.getNo()),
                            earnSpeed,
                            addition,
                            entry.getValue()
                    ));

                    //埋点
                    //GrandChildLog.examination(player, result.getNo(), entry.getKey(), earnSpeed, entry.getValue());
                }
            } catch (Exception e) {
                e.printStackTrace();
            }
        }

        // 同步孙子
        syncGrandChildMsg(syncList);
    }


    private synchronized void addPatronsPupil(GrandChildData grandChildData) {
        int patronsId = grandChildData.getPatronsId();
        Map<Integer, PupilData> dataMap = getPupilDataMap(patronsId);
        int title = grandChildData.getTitle();
        PupilData pupilData = dataMap.get(title);
        if (pupilData == null) {
            pupilData = new PupilData();
            pupilData.setUserId(getUserId());
            pupilData.setPatronsId(patronsId);
            pupilData.setTitle(title);
            pupilData.setNum(0);
            pupilData.setInsertOption();
            dataMap.put(title, pupilData);
        }
        pupilData.setNum(pupilData.getNum() + 1);
    }

    private Map<Integer, PupilData> getPupilDataMap(int patronsId) {
        Map<Integer, PupilData> dataMap = patronsPupilMap.get(patronsId);
        if (dataMap == null) {
            dataMap = new ConcurrentHashMap<>();
            patronsPupilMap.put(patronsId, dataMap);
        }
        return dataMap;
    }

    /**
     * 计算科举结果奖励
     *
     * @param titleMap 孙子ID，title
     */
    private GrandChildExamReward calGrandChildExamReward(Map<String, Integer> titleMap) {
        GrandChildExamReward reward = new GrandChildExamReward();

        List<String> rewardConfigList = StringUtils.stringToStringList(GameConfig.GRAND_CHILD_EXAM_REWARD, "\\|").stream().map(item -> item.split(",")[1]).collect(Collectors.toList());

        for (Map.Entry<String, Integer> entry : titleMap.entrySet()) {
            String childId = entry.getKey();
            int title = entry.getValue();

            GrandChildData grandChildData = grandChildDataMap.get(childId);
            if (grandChildData == null) {
                reward.getLog().add(childId);
                continue;
            }

            Property tmpReward = PropertyHelper.parseStringToProperty(rewardConfigList.get(title - 1));
            // 添加授学经验奖励
            int patronsId = grandChildData.getPatronsId();
            if (patronsId != 0) {
                reward.getTeachReward().addProperty(patronsId, tmpReward.getCountByGoodsId(GameConfig.GRAND_CHILD_TEACH_ITEM_ID));
            } else {
                reward.getLog().add(childId + "，exp=" + tmpReward.getCountByGoodsId(GameConfig.GRAND_CHILD_TEACH_ITEM_ID));
            }
            tmpReward.removeProperty(GameConfig.GRAND_CHILD_TEACH_ITEM_ID);
            // 添加道具奖励
            reward.getGoodsReward().addProperty(tmpReward);
        }

        return reward;
    }

    /**
     * 门客历史上是否有门生
     *
     * @param patronsId
     * @return
     */
    public boolean canInitTeacherSkill(int patronsId) {
        return SystemOpenMgr.systemOpen(player,eSystemId.GrandChild.getValue());
    }

    public Map<Integer, GrandChildProfession> getGrandChildProfessionMap() {
        return grandChildProfessionMap;
    }

    /**
     * 获取职业技能加成
     *
     * @return
     */
    public Map<Integer, BigInteger> getGrandChildProfessionAddAbilityMap() {
        Map<Integer, BigInteger> dataMap = new ConcurrentHashMap<>();
        if (null == grandChildProfessionMap) {
            return dataMap;
        }

        for (GrandChildProfession profession : grandChildProfessionMap.values()) {
            int childOccupation = GrandChildConfigMgr.getChildOccupation(profession.getProfessionId());
            if (0 == childOccupation) {
                log.error("getGrandChildProfessionAddAbilityMap childOccupation is null,professionId:{}", profession.getProfessionId());
                continue;
            }
            int professionSkillId = ConfigMgr.getGrandChildProfessionSkill(childOccupation);
            //技能信息
            SkillInfo skillInfo = SkillMgr.getSkillInfo(professionSkillId);
            if (null == skillInfo) {
                log.error("getGrandChildProfessionAddAbilityMap skillInfo is null,skillId:{}", professionSkillId);
                continue;
            }
            if (skillInfo.getType() == eSkillType.PatronsCookSkill.getValue()) {//加对应职业门客能力技能
                //对应的门客加成
                int param3 = skillInfo.getParamList().get(2);
                BigInteger ability = BigInteger.ZERO;
                if (dataMap.containsKey(param3)) {
                    ability = dataMap.get(param3);
                }
                ability = ability.add(BigInteger.valueOf(SkillMgr.getSkillAddition(skillInfo, profession.getSkillLv())));
                dataMap.put(param3, ability);
            }
        }
        return dataMap;
    }

    public Map<String, GrandChildData> getGrandChildDataMap() {
        return grandChildDataMap;
    }

    public void addTalentBook(List<Integer> talentList) {
        for (Integer talentId : talentList) {
            GrandChildTalentBook talentBook = talentBookMap.get(talentId);
            if (talentBook == null) {
                talentBook = new GrandChildTalentBook();
                talentBook.setUserId(getUserId());
                talentBook.setTalentId(talentId);
                talentBook.setCreateTime(System.currentTimeMillis());
                talentBook.setInsertOption();
                talentBookMap.put(talentId, talentBook);
            }
        }
    }

    public void syncTalentBook() {
        GrandChildProto.GrandChildTalentBookRespMsg.Builder resp = GrandChildProto.GrandChildTalentBookRespMsg.newBuilder();
        Set<Integer> list = talentBookMap.keySet();
        resp.addAllTalentId(list);
        player.sendPacket(ClientProtocol.U_GRANDSON_CHILD_TALENT_BOOK_REQ, resp);
    }

    public void initSystem() {
        checkSchoolSite();
        player.getModule(PatronsModule.class).checkPatronsTeacherSkill(0);
    }

    /**
     * 比较替换职业中最赚钱孙子
     *
     * @param grandChildData
     * @return
     */
    private void compareProfessionGrandChild(GrandChildData grandChildData) {
        if (null == grandChildData) {
            return;
        }
        if (grandChildData.getProfessionId() <= 0) {
            return;
        }
        synchronized (grandChildProfessionTopMap) {
            if (!grandChildProfessionTopMap.containsKey(grandChildData.getProfessionId())) {
                grandChildProfessionTopMap.put(grandChildData.getProfessionId(), grandChildData);
            } else {
                GrandChildData oldData = grandChildProfessionTopMap.get(grandChildData.getProfessionId());
                if (grandChildData.getEarnSpeed().compareTo(oldData.getEarnSpeed()) > 0) {
                    grandChildProfessionTopMap.put(grandChildData.getProfessionId(), grandChildData);
                }
            }
        }
    }

    public int useArchaeusItem(String childId, int useNum) {
             //计算孙子需要多少道具才能毕业
        GrandChildData grandChild = getGrandChild(childId);
        if (grandChild == null) {
            return GameErrorCode.E_GRAND_CHILD_NO_EXIST;
        }
        if (canGraduation(grandChild)) {
            return GameErrorCode.E_GRAND_CHILD_SCHOOL_CAN_GRADUATION;
        }
        if (grandChild.getPatronsId() == 0) {
            return GameErrorCode.E_GRAND_CHILD_NO_ENTER_SCHOOL;
        }
        int needNum = GrandChildConfigMgr.calcGraduationNeedItemNum(grandChild);
        useNum = Math.min(useNum, needNum);
        if (useNum > 0) {
            //判断道具是否足够
            if (!player.getModule(CurrencyModule.class).removeCurrency(new Property(GameConfig.GRAND_CHILD_ARCHAEUS_RECOVER_ITEM_ID, BigInteger.valueOf(useNum)), eLogMoneyType.GrandChild, eLogMoneyType.HoneyMoon)) {
                return GameErrorCode.E_BAG_ITEM_NO_ENOUGH;
            }
        }
        GoodsInfo goodsInfo = GoodsMgr.getGoodsById(GameConfig.GRAND_CHILD_ARCHAEUS_RECOVER_ITEM_ID);
        int recoverNum = goodsInfo.getParamList().get(0).intValue() * useNum;
        //使用道具 执行培养
        grandChild.setArchaeus(recoverNum + grandChild.getArchaeus());
        GrandChildProto.GrandChildUseArchaeusItemRespMsg.Builder resp = GrandChildProto.GrandChildUseArchaeusItemRespMsg.newBuilder();
        resp.setRet(0);
        resp.setGrandChild(GrandChildPb.parseGrandChildMsg(grandChild));
   //     syncGrandChildMsg(Collections.singletonList(grandChild));
        player.sendPacket(ClientProtocol.U_GRAND_CHILD_USE_YUANQI_ITEM, resp);
        return 0;
    }


    public void grandChildRankChange(GrandChildData grandChildData) {
        if(null == grandChildData){
            return;
        }
        boolean hasChange = false;
        if(null == topGrandChild){
            topGrandChild = grandChildData;
            hasChange = true;
        }else {
            if(grandChildData.getEarnSpeed().compareTo(topGrandChild.getEarnSpeed()) > 0){
                topGrandChild = grandChildData;
                hasChange = true;
            }
        }
        try {
            if (hasChange) {
                JSONObject dataObj = new JSONObject();
                dataObj.put("id", topGrandChild.getId());
                dataObj.put("appearanceId", topGrandChild.getAppearanceId());
                dataObj.put("earnSpeed", topGrandChild.getEarnSpeed());
                RankMgr.changeUserRank(eBigRankType.Normal.getValue(), player.getUserId(), topGrandChild.getEarnSpeed(), eRankType.GrandChildEarnSpeed.getValue(), dataObj.toJSONString());
            }
        } catch (Exception ex) {
            ex.printStackTrace();
        }
    }

    public int calcAllGrandChildGraduatedNeedItemNum(int schoolType) {
        List<SchoolSiteData> siteDataList = getSiteDataList(schoolType);
        int totalNum = 0;
        for (SchoolSiteData siteData : siteDataList) {
            String childId = siteData.getChildId();
            GrandChildData grandChild = getGrandChild(childId);
            if (grandChild != null && grandChild.getPatronsId() != 0 && !canGraduation(grandChild)) {
                //还没有毕业
                //重新算下元气值
                GrandChildConfigMgr.ReCalcArchaeus(grandChild);
                int itemNum = GrandChildConfigMgr.calcGraduationNeedItemNum(grandChild);
                totalNum += itemNum;
            }
        }
        return totalNum;
    }

    public int oneKeyTrain(int schoolType) {
        int vipLv = VipMgr.getVipLv(player.getUserInfo().getVipExp());
        if (vipLv < GameConfig.GRAND_CHILD_AUTO_TRAIN && player.getUserInfo().getTitleId() < GameConfig.GRAND_CHILD_AUTO_TRAIN_LV) {
            return GameErrorCode.E_GRAND_CHILD_ONE_KEY_VIP_LIMIT;
        }
        GrandChildProto.SchoolTrainRespMsg.Builder resp = GrandChildProto.SchoolTrainRespMsg.newBuilder();
        int emptySiteNum = getSchoolEmptySiteNum(schoolType);
        List<SchoolSiteData> siteDataList = getSiteDataList(schoolType);
        if (emptySiteNum == siteDataList.size()) {
            return GameErrorCode.E_GRAND_CHILD_NO_CHILD_NEED_TRAIN;
        }
        int totalItemNum = calcAllGrandChildGraduatedNeedItemNum(schoolType);
        if (!player.getModule(CurrencyModule.class).currencyIsEnough(new Property(GameConfig.GRAND_CHILD_ARCHAEUS_RECOVER_ITEM_ID, BigInteger.valueOf(totalItemNum)))) {
            player.notifyListener(eGamePlayerEventType.RechargeGrandChild.getValue(), 0);
            player.notifyListener(eGamePlayerEventType.RechargeGrandChildNew.getValue(), 0);
            return GameErrorCode.E_BAG_ITEM_NO_ENOUGH;
        }
        for (SchoolSiteData siteData : siteDataList) {
            String childId = siteData.getChildId();
            GrandChildData grandChild = getGrandChild(childId);
            if (grandChild != null && grandChild.getPatronsId() != 0 && !canGraduation(grandChild)) {
                //还没有毕业
                //重新算下元气值
                GrandChildConfigMgr.ReCalcArchaeus(grandChild);
                int itemNum = GrandChildConfigMgr.calcGraduationNeedItemNum(grandChild);
                int ret = useArchaeusItem(childId, itemNum);
                log.info("grandChild one key train item totalItemNum: {}, item: {}", totalItemNum, itemNum);
                if (ret != 0) {
                    log.error("grandChild one key train item no enough. totalItemNum: {}", totalItemNum);
                    return ret;
                }
                doTrainResult(resp, siteData, 0);
            }
        }
        if (resp.getResultList().size() <= 0) {
            //没有培养 说明元气不足
            resp.setRet(GameErrorCode.E_GRAND_CHILD_SCHOOL_ARCHAEUS_NO_ENOUGH);
        } else {
            resp.setRet(0);
        }
        player.sendPacket(ClientProtocol.U_GRAND_CHILD_ONE_KEY_TRAIN, resp);
        return 0;
    }

    /**
     * 找回孙子元气
     */
    public RecoveryRewardProto.RecoverGrandSonRespMsg.Builder recoveryGrandChildArchaeus(int days, RecoveryRewardParameterConfig recoveryRewardParameterConfig) {
        RecoveryRewardProto.RecoverGrandSonRespMsg.Builder builder = RecoveryRewardProto.RecoverGrandSonRespMsg.newBuilder();
        List<GrandChildData> syncList = new ArrayList<>();

        int recoveryNeedTime = GameConfig.GRAND_CHILD_ARCHAEUS_RECOVER_TIME * 60;


        for (List<SchoolSiteData> siteDataList : new ArrayList<>(schoolSiteMap.values())) {
            for (SchoolSiteData data : new ArrayList<>(siteDataList)) {
                if (StringUtils.isNullOrEmpty(data.getChildId())) {
                    continue;
                }
                GrandChildData childData = grandChildDataMap.get(data.getChildId());
                if (childData == null) {
                    log.info("玩家Id:{},孙子Uid:{}数据未找到,找回天数{}失败",getUserId(),data.getChildId(),days);
                    continue;
                }
                if (!lastSaveTimePowerMap.containsKey(childData.getId())){
                    log.info("玩家Id:{},孙子Uid:{}登录时未初始化体力数据,找回天数{}失败",getUserId(),data.getChildId(),days);
                    continue;
                }
                //取得最后一次保存的体力
                int lastSaveTimePower = lastSaveTimePowerMap.get(childData.getId());
                //计算需要找回的次数
                int shouldRecoveryTimes = player.getModule(RecoveryRewardModule.class).calculateShouldRetrieveTimesByOneDayRecovery(GameConfig.GRAND_CHILD_ARCHAEUS_LIMIT, recoveryNeedTime, recoveryRewardParameterConfig.getDiscountParameter(),lastSaveTimePower,days) ;
                if (shouldRecoveryTimes <= 0){
                    continue;
                }
                long needArchaeus = GrandChildConfigMgr.calcGraduationNeedArchaeus(childData);
                if (needArchaeus < shouldRecoveryTimes) {
                    shouldRecoveryTimes = (int) needArchaeus;
                }
                if (shouldRecoveryTimes <= 0) {
                    continue;
                }
                int archaeus = childData.getArchaeus();
                childData.setArchaeus(archaeus + shouldRecoveryTimes);
                if (archaeus != childData.getArchaeus()) {
                    syncList.add(childData);
                }
                RecoveryRewardProto.GrandSonArchaeusMsg.Builder singleChildBuilder = RecoveryRewardProto.GrandSonArchaeusMsg.newBuilder();
                singleChildBuilder.setGrandSonName(childData.getNickName());
                singleChildBuilder.setRecoveryArchaeus(shouldRecoveryTimes);
                builder.addArchaeusMsg(singleChildBuilder);
            }
        }
        syncGrandChildMsg(syncList);
        return builder;
    }

    public Map<Integer, GrandChildOfficial> getGrandChildOfficialMap() {
        return grandChildOfficialMap;
    }
    /**
     * 获取学校空学位MAP.K:学校类型，V:空学位
     * @return
     */
    public Map<Integer, List<SchoolSiteData>> getSchoolEmptySiteMap() {
        Map<Integer, List<SchoolSiteData>> map = new HashMap<>();
        for (Map.Entry<Integer, List<SchoolSiteData>> entry : schoolSiteMap.entrySet()) {
            List<SchoolSiteData> list = new ArrayList<>();
            for (SchoolSiteData schoolSiteData : entry.getValue()) {
                if (StringUtils.isNullOrEmpty(schoolSiteData.getChildId())) {
                    list.add(schoolSiteData);
                }
            }
            map.put(entry.getKey(), list);
        }
        return map;
    }

    /**
     * 获取未入学孙子LIST,根据职业。按总天资排序
     * @param occupationType
     * @return
     */
    public List<GrandChildData> getGrandChildByOccupation(int occupationType) {
        List<GrandChildData> list = new ArrayList<>();
        for (GrandChildData grandChildData : grandChildDataMap.values()) {
            //不处理未保存孙子
            if (!grandChildData.getOptionalList().isEmpty()) {
                continue;
            }
            //无入学的孙子，注入list
            if (grandChildData.getOccupation() == occupationType && grandChildData.getPatronsId() <= 0) {
                list.add(grandChildData);
            }
        }
        //按品质排序排序
        list.sort(Comparator.comparingInt(GrandChildData::getAllFlair).reversed());
        return list;
    }

}
